#include "SyncTcpClient.h"
#include <iostream>

using namespace clusnet::net;

SyncTcpClient::SyncTcpClient()
    : io_(),
      socket_(io_)
{
}

SyncTcpClient::~SyncTcpClient()
{
}

void SyncTcpClient::connect(boost::system::error_code& ec, char const* host, char const* port, std::chrono::steady_clock::duration const& timeout)
{
    auto endpoints = boost::asio::ip::tcp::resolver(io_).resolve(host, port);

    boost::asio::async_connect(socket_, endpoints, [&ec](boost::system::error_code const& error, boost::asio::ip::tcp::endpoint const&)
    {
        ec = error;
    });

    run(ec, timeout);
}

TcpUniqDataPacketPtr SyncTcpClient::read(boost::system::error_code& ec, std::chrono::steady_clock::duration const& timeout)
{
    TcpUniqDataPacketPtr msg(new TcpDataPacket);
    boost::asio::async_read(socket_, boost::asio::buffer(msg->header(), msg->headerLength()), [&ec, &msg, this](boost::system::error_code const& error, std::size_t)
    {
        ec = error;
        if (!ec)
        {
            msg->decodeHeader();
            boost::asio::async_read(socket_, boost::asio::buffer(msg->body(), msg->bodyLength()), [&ec](boost::system::error_code const& error, std::size_t)
            {
                ec = error;
            });
        }
    });

    run(ec, timeout);
    return msg;
}

size_t SyncTcpClient::write(boost::system::error_code& ec, TcpDataPacketPtr& data, std::chrono::steady_clock::duration const& timeout)
{
    size_t len = 0;
    data->encodeHeader();
    boost::asio::async_write(socket_, boost::asio::buffer(data->cdata(), data->dataLength()), [&ec, &len](boost::system::error_code const& error, std::size_t length)
    {
        ec = error;
        len = length;
    });

    run(ec, timeout);
    return len;
}

void SyncTcpClient::run(boost::system::error_code& ec, std::chrono::steady_clock::duration const& timeout)
{
    io_.restart();

    if (timeout == std::chrono::seconds(0))
    {
        io_.run(); 
        return;
    }
    else
    {
        io_.run_for(timeout);

        if (!io_.stopped())
        {
            socket_.close();
            io_.run();
            ec = boost::asio::error::timed_out;
        }
    }
}

void SyncTcpClient::close()
{
    socket_.close();
}
